복사 생략(Copy Elision)C++11에서 공식화된 기능으로 컴파일러가 복사 또는 이동 연산자를 회피 할 수 있으면 회피하는 것을 허용하는 방식이다.
특정 조건을 만족하면, 컴파일러가 임의로 최적화를 위해 복사 및 이동 연산을 생략한다.
- Return Value Optimization(반환값 최적화)인 경우
- Passing Temporary as Value
Return Value Optimization/ Named Return Value OptimizationRVO/NRVO 는 함수의 반환값이 특정 객체의 값 형식일 때, 복사 생성을 회피할 수 있는 컴파일러 최적화를 의미한다.
#include <iostream>
struct Foo{
Foo(void){ std::cout<<"Constructed\n"; }
Foo(const Foo&){ std::cout<<"Copy-Constructed\n"; }
Foo(Foo&&){std::cout<<"Move-Constructed\n"; }
~Foo(void){ std::cout<<"Destructed\n"; }
};
Foo RVO_F(void){
return Foo();
}
Foo NRVO_F(void){
Foo foo;
return foo;
}
int main(void){
{
Foo rvo_foo=RVO_F();
}
{
Foo nrvo_foo=NRVO_F();
}
return 0;
}
Constructed
Destructed
Constructed
Destructed
복사 생략이 되지 않다면, RVO에 대해서 return을 통해 값이 복사될 때, 사용되는 복사생성자가 호출되어야 한다.
NRVO의 경우, foo를 rvo_foo로 이동할 때, 복사 생성자가 호출되며, 함수가 끝난 후, foo 또한 소멸자가 호출되어
2번 소멸자가 호출되어야 한다.
Passing Temporary as Value함수 인자로써 임시 객체를 값으로 전달하는 경우
#include <iostream>
struct Foo{
Foo(void){ std::cout<<"Constructed\n"; }
Foo(const Foo&){ std::cout<<"Copy-Constructed\n"; }
Foo(Foo&&){std::cout<<"Move-Constructed\n"; }
~Foo(void){ std::cout<<"Destructed\n"; }
};
void f(Foo f){
std::cout<<"Fn\n";
}
int main(void){
f(Foo());
return 0;
}
Constructed
Fn
Destructed
복사 생략이 되지 않았다면, f 함수에 인자로 전달하기 위해 Rvalue Foo를 이용해서 이동생성자를 호출해야 한다.
(Rvalue Foo()에서 f로 값을 이동)
하지만, 컴파일러는 최적화를 통해 임시 객체가 참조되는 대상 없이 함수의 인자로 넘어갈 때(값으로 전달될 때),
복사/이동 생성자가 생략된다.